package cn.biplam.back.operator.tree;

import java.util.ArrayList;

import cn.biplam.back.sql.dml.Insert;
import cn.biplam.common.client.DataFilter;
import cn.biplam.common.client.DbResource;
import cn.biplam.common.client.Operator;
import cn.biplam.common.client.OperatorResult;
import cn.biplam.common.client.Sequence;
import cn.biplam.common.opcode.SysManageCode.TreeIndexCode;
import cn.biplam.common.paramters.TreeParam.Index_Tree_Node_Type;
import cn.biplam.common.tools.TOOLS;
import cn.biplam.common.wrapper.ErrorMessage;
import cn.biplam.common.wrapper.tree.TreeIndexFields;

/**
 * 索引树数据操作—插入节点
 * @author XiaoRuxing Nov 9, 20094:46:52 PM
 * @version 1.0
 */
public class OptIndexTreeInsert extends IndexTree{

	//节点类型
	private String type ;
	//节点索引类型
	private String nodeType ;
	//节点ID
	private String nodeId ;
	//父亲节点ID
	private String parentId;
	//节点名称
	private String nodeName ;
	//节点描述
	private String nodeDesc ;
	//节点真实ID
	private String nodeRealId ;
	//排序号
	private int sequence;

	protected OptIndexTreeInsert(String userId, String tableName,
			TreeIndexFields fields, DbResource dbResource) {
		super(userId, tableName, fields, dbResource);
	}

	/**
	 * 传入父亲真实ID插入节点<BR><font color='red'>(不建议使用,目前只在模型索引树使用)</font><BR>
	 *  参数:<BR>父节点真实ID(使用NODE_PARENT名称传参),[节点ID],[节点类型](OperatorType),[名称],[描述],[节点真实ID],[index]<BR>
	 * @throws ErrorMessage 
	 */
	protected String insertByParentRealId(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		//父亲节点真实ID
		String parentRelID=filter.getValue(fields.getNodeParent());
		/** 根据父节点物理表ID获取父节点信息 */
		DataFilter parentFilter=new DataFilter();
		parentFilter.put(OptTreeIndex.TABLE_NAME,filter.getValue(OptTreeIndex.TABLE_NAME));
		parentFilter.put(fields.getRealId(), parentRelID);
		OperatorResult sr=Operator.get(TreeIndexCode.Select_By_Rel_ID, userId, parentFilter);
		//获取父亲节点索引ID
		String parentId=null;
		if(TOOLS.exist(sr)){
			parentId = sr.getValue(fields.getNodeId());
		}
		this.insertFirst(sql, filter, parentId);
		return this.nodeId;
	}

	/**
	 *  传入父亲索引ID插入节点<BR>
	 *  参数:<BR>父节点索引ID,[节点ID],[节点类型](OperatorType),[名称]、[描述]、[节点真实ID]、[index] <br>
	 * @throws ErrorMessage 
	 */
	protected String insertByParentIndexId(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		String parentId=filter.getValue(fields.getNodeParent());
		this.insertFirst(sql, filter, parentId);
		return this.nodeId;
	}

	/**
	 *  插入根节点,<B>此接口已经过时不建议使用</B><BR>
	 *  参数:<BR> [节点ID],[父节点ID],[节点类型](OperatorType),[名称]、[描述]、[节点真实ID]、[index](索引类型) <br>
	 *        <li>父亲节点ID不传时默认父亲节点ID为-1(根节点的父亲为虚拟根节点,不建议不传父亲节点ID)<br>
	 *        <li>节点ID不传时自动生成<br>
	 *        <li>节点类型不传时默认为 "1"<br>
	 *        <li>在模型索引表中的根节点插入必须传入[index](索引类型)参数<br>
	 * @throws ErrorMessage 
	 */
	protected String insertRoot(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		// 父亲节点ID不传时默认父亲节点ID为-1
		String parentId=(filter.getValue(fields.getNodeParent()));
		if(TOOLS.StringIsEmpty(parentId)){
			parentId = "-1";
		}
		//节点类型不传时默认为"1"
		String operatorType = filter.getValue(fields.getOperatorType());
		if(operatorType==null){
			filter.put(fields.getOperatorType(), Index_Tree_Node_Type.ROOT_THEME);
		}
		this.insertFirst(sql, filter, parentId);
		return this.nodeId;
	}

	/**
	 *  插入虚拟根节点<BR>
	 *  <P>参数: [节点ID],[节点名称],[节点描述] <BR>
	 *        全为可选参数,如果不传入节点ID自动生成节点ID</P>
	 * @throws ErrorMessage 
	 */
	protected String insertVirtualNode(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		this.nodeId = filter.getValue(fields.getNodeId());
		this.parentId = "#-100";
		this.nodeName = filter.getValue(fields.getNodeName());
		this.nodeRealId = filter.getValue(fields.getRealId());
		this.nodeDesc = filter.getValue(fields.getNodeDesc());
		this.sequence = 0;
		this._insert(sql);
		return this.nodeId;
	}

	/**
	 * 	
	 *  获取第一次插入节点的SQL(不适用于虚拟根节点)<BR>
	 *  参数:节点类型 (OperatorType), [节点ID],[父节点ID],[名称]、[描述]、[节点真实ID]、[index] <BR>
	 *      节点类型为必须传入参数<br>
	 * @throws ErrorMessage 
	 */
	protected String insertFirst(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		//节点类型
		String type = filter.getValue(fields.getOperatorType());
		//根据节点类型调用插入
		if(type.equals(Index_Tree_Node_Type.ROOT_THEME.toString())){
			this.insertRoot(sql, filter);//根节点插入
		}else{
			this.insertByParentIndexId(sql, filter);//非根节点插入
		}
		return this.nodeId;
	}

	/**
	 *  <ul>  获取非第一次插入节点的SQL(不适用于虚拟根节点)
	 *    <li>适用条件:获取批量插入节点SQL时插入当前级别非第一个节点
	 *    <li>参数:节点类型 (OperatorType),sequence(前一节点的排序号),
	 *             [哥哥节点类型](参数名为preOperator_Type),
	 *             [父节点ID],[节点ID],[名称]、[描述]、[节点真实ID]、[index] <br>
	 *             sequence为必传字段,父亲节点ID为必传字段
	 *  </ul>
	 * @param sql
	 * @param filter 
	 * @throws ErrorMessage 
	 */
	protected String insertSecond(ArrayList<String> sql,
			DataFilter filter) throws ErrorMessage{
		//获取节点基本信息
		getNodeParams(filter);
		this.parentId = filter.getValue(fields.getNodeParent());
		//当前节点的排序为外部传入排序+1
		String sSequence = filter.getValue(fields.getNodeSequence());
		this.sequence = getLBrotherSequence(
				filter.getValue("pre"+this.fields.getOperatorType()), //当前节点哥哥的节点类型
				this.type, //当前节点类型
				TOOLS.stringToInt(sSequence)); //当前节点哥哥的排序号
		//生成插入节点的SQL
		_insert(sql);
		return this.nodeId;
	}

	/**
	 *  根据当前节点类型,哥哥节点类型和哥哥节点排序获取当前节点排序
	 * @return 如果当前节点类型和哥哥节点类型相同,返回哥哥节点排序+1
	 *         如果当前节点类型和哥哥节点类型不同,返回当前节点类型的第一个排序
	 */
	private int getLBrotherSequence(String brotherOperatorType,
			String lBrotherOperatorType,
			int brotherSequence){
		if(brotherOperatorType==null)
			brotherOperatorType = lBrotherOperatorType;//如果未传入当前节点哥哥类型,默认当前节点类型和哥哥节点类型相同
		if(lBrotherOperatorType.equals(brotherOperatorType)){
			return brotherSequence+1;//如果当前节点类型和哥哥节点类型相同,返回哥哥节点排序+1
		}else{
			return getOrder(lBrotherOperatorType, 1); //如果当前节点类型和哥哥节点类型不同,返回当前节点类型的第一个排序
		}
	}

	/**
	 *  
	 * @return 获取当前插入节点的排序号
	 */
	protected int getCurentSequence(){
		return this.sequence;
	}

	/**
	 *<ul> 生成插入索引表节点SQL
	 *<li> 适用条件:直接将节点插入数据库表,获取批量插入节点SQL时插入当前级别第一个节点
	 *<li> 1 根据类型生成排序号<br>
        	主题：父亲的子主题个数+1 <br>
        	叶子节点类型：父亲的叶子类型子节点个数+5000 <br>
	 *</ul>
	 * @param sql         
	 * @param filter      节点信息: 节点ID、父节点ID、OperatorType、[名称]、[描述]、[节点真实ID]、[index] (方括号为可选参数,其他为必选参数)<br>
	 *                    标志字段: sequence<br>
	 * @param parentLevel 父节点层次
	 * @throws ErrorMessage 
	 */
	private void insertFirst(ArrayList<String> sql,
			DataFilter filter,
			String parentId
	) throws ErrorMessage{
		//获取插入节点基本信息
		getNodeParams(filter);
		this.parentId = parentId;
		String nodeType = null;
		if(this.type.equals(Index_Tree_Node_Type.ROOT_THEME.toString()))
			nodeType = this.nodeType;//节点类型为根节点时获取兄弟节点传入索引类型(为了兼容model_index)
		//根据节点类型生成节点排序号
		this.sequence= orderNumByOperatorType(parentId, nodeType, this.type);
		//生成插入节点的SQL
		_insert(sql);
	}

	/**
	 *  获取节点基本信息
	 */
	private void getNodeParams(DataFilter filter){
		//节点索引类型
		this.nodeType = filter.getValue(fields.getNodeType());
		//节点类型
		this.type = filter.getValue(fields.getOperatorType());
		//节点ID
		this.nodeId = filter.getValue(fields.getNodeId());
		//节点名称
		this.nodeName = filter.getValue(fields.getNodeName());
		//节点描述
		this.nodeDesc = filter.getValue(fields.getNodeDesc());
		//节点真实ID
		this.nodeRealId = filter.getValue(fields.getRealId());
	}

	/**
	 *  生成插入节点SQL
	 * @throws ErrorMessage
	 */
	private void _insert(ArrayList<String> sql) throws ErrorMessage{
		Insert insert=new Insert(tableName);
		if(nodeId==null){//如果节点ID未设置，自动生成节点ID
			nodeId = Sequence.getSequence(userId);
		}
		insert.putStringFieldDenyNull(fields.getNodeId(), nodeId);//节点ID不能为空
		insert.putStringFieldIgnoreNull(fields.getNodeParent(), parentId);
		insert.putStringFieldIgnoreNull(fields.getNodeName(), nodeName);
		insert.putStringFieldIgnoreNull(fields.getNodeDesc(), nodeDesc);
		insert.putFieldDenyNull(fields.getNodeSequence(), String.valueOf(sequence));//节点排序号不能为空
		insert.putStringFieldIgnoreNull(fields.getRealId(),nodeRealId);
		insert.putStringFieldIgnoreNull(fields.getOperatorType(), type);
		insert.putStringFieldIgnoreNull(fields.getNodeType(), nodeType);
		sql.add(insert.getSimpleSql());
	}
}
